#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <fcntl.h>
#include "../comm/util.hpp"
#include "../comm/log.hpp"

namespace NS_Runner
{
    using namespace NS_Util;
    using namespace NS_Log;
    class Runner
    {
    public:
        Runner()
        {};
        ~Runner()
        {};

    public:
        // 设置子进程运行占用资源的大小
        static void SetProcLimit(int cpu_limit, int mem_limit)
        {
            struct rlimit cpu_rl;
            cpu_rl.rlim_max = RLIM_INFINITY;
            cpu_rl.rlim_cur = cpu_limit;
            setrlimit(RLIMIT_CPU, &cpu_rl);

            struct rlimit mem_rl;
            mem_rl.rlim_max = RLIM_INFINITY;
            mem_rl.rlim_cur = mem_limit * 1024; // 转化成KB
            setrlimit(RLIMIT_AS, &mem_rl);
        }

    public:
        /*****************************************
         * 运行完我们最关心的问题就是运行结果：
         * 1. 运行完结果对
         * 2. 运行完结果不对
         * 3. 异常结束
         *
         * 运行模块不需要考虑代码运行的结果是否正确，只关心程序是否正常退出。程序的退出信息交给上层模块处理。
         * 返回值 > 0: 程序异常，返回异常信号
         * 返回值 = 0: 程序正常退出，结果已写入文件
         * 返回值 < 0: 内部错误
         *
         * 其次我们要控制程序的输入输出，
         * 标准输入：不作处理
         * 标准输出：一般是程序运行的结果
         * 标准错误：运行时错误信息
         *
         * 然后我们要控制程序时间空间复杂度，CPU时间及空间占用，具体的数值由测试用例决定，由上层传入
        *****************************************/
        static int Run(const std::string& file_name, int cpu_limit, int mem_limit)
        {
            std::string exe_file = PathUtil::Exe(file_name);
            int sin_fd = FileUtil::OpenFile(PathUtil::Stdin(file_name));
            int sout_fd = FileUtil::OpenFile(PathUtil::Stdout(file_name));
            int serr_fd = FileUtil::OpenFile(PathUtil::Stderr(file_name));

            if (sin_fd == 0 || sin_fd == 0 || serr_fd == 0)
            {
                LOG(ERROR) << "runner open standard file failed\n";
                return -1;
            }

            pid_t pid = fork();
            if (pid == 0) /* child */
            {
                //重定向标准输出输入错误
                dup2(sin_fd, 0);
                dup2(sout_fd, 1);
                dup2(serr_fd, 2);
                // 设置资源约束
                SetProcLimit(cpu_limit, mem_limit);

                std::cout << "for stdout" << std::endl;

                // ./tmp/code.out
                execl(exe_file.c_str(), exe_file.c_str());
                exit(-2);
            }
            else if (pid > 0) /* parent */
            {
                int status = 0;
                waitpid(pid, &status, 0);

                LOG(INFO) << "runner executed success, return: " << (status & 0x7F) << "\n";
                return status & 0x7F; // 返回程序的退出信息，具体情况交给上层处理
            }
            else /* error */
            {
                close(sin_fd);
                close(sout_fd);
                close(serr_fd);

                LOG(ERROR) << "runner create child proccess failed\n";
                exit(-3);
            }
        }

    };
}